<?php

error_reporting(E_ALL);

/**
 * @mainpage Commic.solor.cn Model Page
 * 
 * @section About 关于
 *
 * 这个版本的一些文件来源于我之前做过的一个叫做hawk的项目
 *
 *
 * @section daovo BasicDAO and BasicVO
 * <ul>
 * <li>BasicDAO : DataAccess Object</li>
 * <li>BasicVO  : Value Object </li>
 * </ul>
 *
 * The main implementation of these two object is in BasicDAO and BasicVO
 *
 * 出了这两个基本的类之外,ComicFacade 类对常用的操作进行了风转
 * <ul>
 * <li>ComicFacade : 常用的Comic.solor.cn的操作</li>
 * </ul>
 *
 */


/**
 * HawkModel - BasicDAO.php
 *
 * $Id: BasicDAO.php,v 1.1.1.1 2007-10-02 13:32:35 rainx Exp $
 *
 * This file is part of HawkModel.
 *
 * Automatic generated with ArgoUML 0.22 on 06.11.2006, 11:26:02
 *
 * @author RainX
 */

if (0 > version_compare(PHP_VERSION, '5')) {
    die('This file was generated for PHP 5');
}

/**
 * include DB
 *
 * @author firstname and lastname of author, <author@example.org>
 */
//require_once('DB.php');

//require_once('BasicVO.php');

//require_once('Logger.php');

/**
 * include DAO
 *
 * @author RainX
 * @version Hawk Data acess interface
 */
//require_once('DAO.php');

/* user defined includes */
// section 127-0-0-1--238dec88:10ea8103daf:-8000:0000000000000838-includes begin
// section 127-0-0-1--238dec88:10ea8103daf:-8000:0000000000000838-includes end

/* user defined constants */
// section 127-0-0-1--238dec88:10ea8103daf:-8000:0000000000000838-constants begin
// section 127-0-0-1--238dec88:10ea8103daf:-8000:0000000000000838-constants end

class BasicDAO
        implements DAO
{
    // --- ATTRIBUTES ---

    /**
     * Short description of attribute db
     *
     * @access protected
     * @var DB
     */
    protected $db = null;

    /**
     * Short description of attribute tname
     *
     * @access public
     * @var string
     */
    protected $tname = '';

	/**
	 * the instance of Logger object;
	 * @access protected
	 */

	protected $logger;

    // --- OPERATIONS ---
	/**
	 * init DAO object
	 * 
	 * @construct
	 * @param table name of your DAO, default '' 
	 */
	function __construct($tname='')
	{
		$this->db = DB::instance();
		if (empty($this->tname))
		{
			$this->tname = $tname;
		}
		$this->logger = Logger::instance();
	}


	/**
	 * set tname of DAO
	 * @param $tname the table name to set
	 */
	function setTname($tname)
	{
		$this->tname = $tname;
	}

	/**
	 * @return the table name of DAO
	 */
	function getTname()
	{
		return $this->tname;
	}

    /**
	 * get vo array by your specified condition
	 *
	 * @code
	 * $this->testAddVo();
	 * $vo = new BasicVO();
	 * $vo->setAllowedKey(array('name', 'description'));
	 * $vo->set('name' , "testfilter");
	 * $ret = $this->dao->getByCol(array('name'), array($vo));
	 * $this->assertTrue(count($ret) > 0);
	 * $this->assertEquals($ret[0]->get("name"), 'testfilter');
	 * @endcode
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param array $keys the key list in your condition . eg  array('id')
     * @param array $cols the array of VO in condtion 
     * @return array
     */
    public function getByCol($keys, $cols)
    {
        $returnValue = array();

		$sql = "select * from {$this->tname} where ";

		$values = array();

		foreach ($cols as $col)
		{
			$sql .= "(";
			foreach ($keys as $key )
			{
				$k = $key;
				$v = $col->get($key);
				$sql .= "$k=? and ";
				$values[] = $v;
			}
			$sql = substr($sql,0, strlen($sql) - 4);
			$sql .= ") or ";
		}
		$sql = substr($sql,0, strlen($sql) - 3);
		$sql .= ";";

		if ( C_DEBUG ) $this->log($sql);
		$stmt = $this->db->prepare($sql)
			or $this->log("prepare error in getByCols function", C_PDO_PREPARE_ERROR);
		$stmt->execute($values)
			or $this->log("excute error in getByCols function", C_PDO_EXECUTE_ERROR);
		while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
		{
			$vo = $this->produceVO();
			$vo->setEntity($row);
			$returnValue[] = $vo;
		}	

        return (array) $returnValue;
    }

    /**
     * Short description of method setByCol
	 *
	 * @code
	 * $this->testAddVo();
	 * $vo = new BasicVO();
	 * $vo->setAllowedKey(array('name', 'description'));
	 * $vo->set('name' , "testfilter");
	 * $this->dao->setByCol(array("name"), array($vo), 'name', 'newvalue');
	 * $vo->set('name' , 'newvalue');
	 * $ret = $this->dao->getByCol(array('name'), array($vo));
	 * $this->assertEquals($ret[0]->get("name"), 'newvalue');
	 * @endcode
	 *
	 *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param $keys the key list in your condition . eg  array('id')
     * @param $cols the array of VO in condtion 
     * @param $setKey the column to be updated
     * @param $setValue the value of the column to be updated
     * @return int
     */
    public function setByCol($keys, $cols, $setKey, $setValue)
    {
        $returnValue = (int) 0;

		$values = array();
		$sql = "update {$this->tname} set $setKey=? where ";

		$values[] = $setValue;

		foreach ($cols as $col)
		{
			$sql .= "(";
			foreach ($keys as $key )
			{
				$k = $key;
				if ( is_int($k)) continue;
				$v = $col->get($key);
				$sql .= "$k=? and ";
				$values[] = $v;
			}
			$sql = substr($sql,0, strlen($sql) - 4);
			$sql .= ") or ";
		}
		$sql = substr($sql,0, strlen($sql) - 3);
		$sql .= ";";
		if ( C_DEBUG ) $this->log($sql);
		$stmt = $this->db->prepare($sql)
			or $this->log("Prepare error in setByCol", C_PDO_PREPARE_ERROR);
		$returnValue = $stmt->execute($values)
			or $this->log("Execute error in setByCol", C_PDO_PREPARE_ERROR);

        return $returnValue;
    }

	/**
	 * Use SQL "replace into" to replace your vo to corresponding one in db. 
	 *
	 * @param $vo The value object to be put in db
	 */
	public function replace($vo)
	{
		$entity = $vo->getEntity();
		$sql = "replace into {$this->tname} set ";
		$binds = array();

		foreach ($entity as $k=>$v)
		{
			if ( is_int($k)) continue;
			$sql  .= "$k = ? , ";
			$binds[] = $v;
			$processed = true;
		}
		if ($processed)
		{
			$sql = substr($sql, 0, strlen($sql) - 3);
		}

		if ( C_DEBUG ) $this->log($sql);
		$stmt = $this->db->prepare($sql)
			or $this->log("Prepare error in replace", C_PDO_PREPARE_ERROR);

		$stmt->execute($binds)
			or $this->log("Execute error in replace", C_PDO_EXECUTE_ERROR);
	}

    /**
     * delete specified row by your condition
	 *
	 * @code
	 * $this->testRunSql();
	 * $this->testAddVo();
	 * $vo = new BasicVO();
	 * $vo->setAllowedKey(array('name', 'description'));
	 * $vo->set('name' , "testfilter");
	 * $this->dao->rmByCol(array('name'), array($vo));
	 * $all = $this->dao->getAll();
	 * $this->assertEquals(count($all) , 0);
	 * @endcode
	 *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param array $keys the key list in your condition . eg  array('id')
     * @param array $cols the array of VO in condtion 
     * @return int
     */
    public function rmByCol($keys, $cols)
    {
        $returnValue = (int) 0;
		
		$sql = "delete from {$this->tname} where ";

		$values = array();

		foreach ($cols as $col)
		{
			$sql .= "(";
			foreach ($keys as $key )
			{
				$k = $key;
				if ( is_int($k)) continue;
				$v = $col->get($key);
				$sql .= "$k=? and ";
				$values[] = $v;
			}
			$sql = substr($sql,0, strlen($sql) - 4);
			$sql .= ") or ";
		}
		$sql = substr($sql,0, strlen($sql) - 3);
		$sql .= ";";

		if ( C_DEBUG ) $this->log($sql);
		$stmt = $this->db->prepare($sql)
			or $this->log("Prepare error in rmByCol", C_PDO_PREPARE_ERROR);
		$stmt->execute($values)
			or $this->log("Execute error in rmByCol", C_PDO_EXECUTE_ERROR);
        return (int) $returnValue;
    }

    /**
     * Short description of method add
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param $obj the Value Object you need to be set to db
     * @return true if successful
     */
    public function add( VO $obj)
    {
        $returnValue = (int) true;
		try
		{
			$entity = $obj->getEntity();
			$processed = false;

			if (empty($this->tname))
			{
				return C_MODEL_NO_TABLE_NAME;
			}
			$sql = "insert into {$this->tname} set ";
			$binds = array();

			foreach ($entity as $k=>$v)
			{
				if ( is_int($k)) continue;
				$sql  .= "$k = ? , ";
				$binds[] = $v;
				$processed = true;
			}
			if ($processed)
			{
				if ( C_DEBUG ) $this->log($sql);
				$sql = substr($sql, 0, strlen($sql) - 3);
			}

			$stmt = $this->db->prepare($sql)
				or $this->log("Prepare error in add", C_PDO_PREPARE_ERROR);
			
			$stmt->execute($binds)
				or $this->log("Execute error in add", C_PDO_EXECUTE_ERROR);
		}
		catch ( PDOException $e)
		{
			return false;
		}
        return (int) $returnValue;
    }

    /**
     * run no-select sql 
     *
     * @access public
     * @author rainx
	 * @param $sql the SQL statement to be run
     * @return returns the number of rows that were modified or deleted by the SQL statement you issued. If no rows were affected, returns 0;
     */
    public function sql($sql)
    {
        $returnValue = (int) 0;
		try 
		{
			if ( C_DEBUG ) $this->log($sql);
			$returnValue  = $this->db->exec($sql);
		}
		catch ( PDOException $e)
		{
			$this->log("run sql err ", C_PDO_EXECUTE_ERROR);
			return C_PDO_EXECUTE_ERROR;
		}
    }

    /**
	 * Get one vo in table by specified condition 
	 *   eg. ->getOneBy('id', 10);
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param $key the Key is the column name in table you wanna to search 
	 * @param $val the value of the table column you want to fetch. 
     * @return VO
     */
    public function getOneBy($key, $val)
    {
        $returnValue = null;

		$sql = "select * from {$this->tname} where $key=?";
		if ( C_DEBUG ) $this->log($sql);
		$stmt = $this->db->prepare($sql)
			or $this->log("Prepare error in getOneBy", C_PDO_PREPARE_ERROR);
		$stmt->execute(array($val))
			or $this->log("Prepare error in getOneBy", C_PDO_EXECUTE_ERROR);
		$result = $stmt->fetch(PDO::FETCH_ASSOC);
		if ($result)
		{
			$vo = $this->produceVO();
			$vo->setEntity($result);
			$returnValue = &$vo;
		}
		else
		{
			$returnValue = null;
		}

        return $returnValue;
    }

    /**
     * get all rows in one table
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @return the vo array of the whole table
     */
    public function getAll()
    {
        $returnValue = array();

		$sql = "select * from {$this->tname}";
		if ( C_DEBUG ) $this->log($sql);
		foreach ($this->db->query($sql) as $row)
		{
			$vo = $this->produceVO();
			$vo->setEntity($row);
			$returnValue[] = $vo;
		}
	
        return (array) $returnValue;
    }

    /**
     * Get all record of one column in table and combine them to a array
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param $key the column you wanna to fetch
     * @return array
     */
    public function getList($key)
    {
        $returnValue = array();

		$sql = "select $key	from {$this->tname}";
		if ( C_DEBUG ) $this->log($sql);
		foreach ($this->db->query($sql) as $row)
		{
			$returnValue[] = $row[$key];
		}

        return (array) $returnValue;
    }

    /**
     * Get associate array with specified two column
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param $key the Key column
     * @param $value the value column
     * @return $the array of key/value pair
     */
    public function getKeyValue($key, $value)
    {
        $returnValue = array();

		$sql = "select $key, $value	from {$this->tname}";
		if ( C_DEBUG ) $this->log($sql);
		foreach ($this->db->query($sql) as $row)
		{
			$returnValue[$row[$key]] = $row[$value];
		}

        return (array) $returnValue;
    }

    /**
     * Short description of method searchByCol
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
     * @param array
     * @param array
     * @return array
     */
    public function searchByCol($keys, $cols)
    {
        $returnValue = array();

		$sql = "select * from {$this->tname} where ";

		$values = array();

		foreach ($cols as $col)
		{
			$sql .= "(";
			foreach ($keys as $key )
			{
				$k = $key;
				$v = $col->get($key);
				// FIX BUG add slashes to  _ and %
				$v = str_replace('_', '\_', $v);
				$v = str_replace('%', '\%', $v);
				// FIX BUG DONE
				$sql .= "$k like ? or  ";
				$values[] = "%{$v}%";
				if ( C_DEBUG ) $this->log("%{$v}%");
			}
			$sql = substr($sql,0, strlen($sql) - 4);
			$sql .= ") or ";
		}
		$sql = substr($sql,0, strlen($sql) - 3);
		$sql .= ";";

		if ( C_DEBUG ) $this->log($sql);
		$stmt = $this->db->prepare($sql)
			or $this->log("prepare error in searchByCols function", C_PDO_PREPARE_ERROR);
		$stmt->execute($values)
			or $this->log("excute error in searchByCols function", C_PDO_EXECUTE_ERROR);
		while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
		{
			$vo = $this->produceVO();
			$vo->setEntity($row);
			$returnValue[] = $vo;
		}	



        return (array) $returnValue;
    }

    /**
     * get basic VO for DAO, you could overwrite it in your sub class
     *
     * @access public
     * @author firstname and lastname of author, <author@example.org>
	 * @return basic VO for DAO
     */
    public function produceVO()
    {
		return new BasicVO();
    }

	/**
	 * Merge two array;
	 * 
	 * @param $i array to be merged 
	 * @param $j another array
	 */
	public function &arrayMerge(&$i, &$j)
	{
		foreach ($j as $key)
		{
			if (!in_array($key, $i))
			{
				$i[] = $key;
			}
		}
		return $i;
	}

	protected function log($msg, $errCode = 0)
	{
		$this->logger->log($msg, $errCode, __FILE__, __LINE__);
	}

} /* end of class BasicDAO */

?>
